var Tasklog=Tasklog||{}
Tasklog.Task = (function(Tasklog){ return {
	autosave:{},
	init: function(idx, item){
		var self=this;
		$('.task-description', item).click(function(e){ if (e.target==this) $('div',this).trigger('focus') }) // focus editable div on td click
		$('.task-description div', item).focus(this.rowEdit);
		$('.task-table .task-description div').unbind('keydown').bind('keydown',function(e){ self.keyHandler.call(self,e,this) }); //scope: this, params: event, div
		$('.task-table .task-description div').unbind('keyup').bind('keyup',function(e){ self.keyUpHandler.call(self,e,this) });
		$('.task-table .task-description div').unbind('paste').bind('paste', function(e){ self.onPaste.call(self,e,this) });
		
		$('.task-check div', item).unbind('click').click(function(){self.check.call(self,item)}); // close / reopen
	}
	
	
	,save: function(row){
		var id=row.attr('id');
		var desc=row.find('.task-description div');
		if (!desc.text().length) return ;
		var rec={ 
			description: desc.html()?desc.html():""
			//,status: row.hasClass('task-done')?1:0
		}
		var dbres, errtype='update';
		if (id) dbres=Tasklog.DB.update(id, rec);
		else{
			errtype='add'
			rec.priority=row.index()+1;
			dbres=Tasklog.DB.add(rec)
			row.attr('id', dbres) // update row's id to the new db.rec.id
		}
		if (!dbres) msgerror('save error: '+errtype);
		//else console.info('saved') 
	}
	
	,ddEnd: function(item,next){
		if (!next) next={id:'last'}
		var dbres=Tasklog.DB.updateDD(item.id, next.id);
		if (!dbres) msgerror('dd update error');
	}

	,moveRowUp: function(row){
		var prev=row.prev('.task');
		if (!prev.length) return;
		row.insertBefore(prev);
		row.find('.task-description div[contenteditable]').focus();
		try{window.getSelection().collapseToEnd()}catch(e){}
		var dbres=Tasklog.DB.updateDD(row.attr('id'), prev.attr('id'));
		if (!dbres) msgerror('dd update error');
	}

	,moveRowDown: function(row){
		var next=row.next('.task');
		if (!next.length) return;
		row.insertAfter(next);
		row.find('.task-description div[contenteditable]').focus();
		try{window.getSelection().collapseToEnd()}catch(e){}
		var dbres=Tasklog.DB.updateDD(row.attr('id'), next.attr('id'));
		if (!dbres) msgerror('dd update error');
	}
	
	,rowEdit: function(e){ 
		var task=$(this).parents('.task');
		task.siblings('.task').removeClass('task-edit') 
		task.addClass('task-edit')
	}

	,rowAdd: function(el, rel){
		if (!rel) rel='after';
		var newRow=$('<tr class="task"><td class="task-dd"></td><td class="task-check"><div></div></td><td class="task-description"><div contenteditable="true"></div></td></tr>');
		if (rel=='after') el.after(newRow);
		else if (rel=='append') el.append(newRow);
		else if (rel=='prepend') el.prepend(newRow);
		this.init(1, newRow);
		newRow.find('.task-description div').focus();
	}

	,rowRemove: function(row,forceDown){ // on key up/down
		rowTR=$(row);
		if (!rowTR.hasClass('task')) rowTR=rowTR.parents('.task');
		var tasksLen=rowTR.siblings('.task').length, div=rowTR.find('div[contenteditable]')[0];
		if (tasksLen>0){ // more than 1 sibling 
			if (rowTR.index()==0 || (forceDown && rowTR.index()<tasksLen)) this.down(div); 
			else this.up(div); 
		}
		this.delRow(rowTR)
	}
	
	//action: fadeOut, clear, remove
	,delRow: function(row){
		var self=this, tasksLen=row.siblings('.task').length, rowID=row.attr('id');
		if (tasksLen>0) row.fadeOut(400,function(){ 
			var tab=row.parents('.task-table tbody');
			row.remove();
			if (!tab.find('.task').length) self.rowAdd(tab,'append');
		});
		else{
			this.check(row,true);
			row.attr('id','0')
			var div=row.find('.task-description div[contenteditable]');
			div.html('<br>');
			div.focus();
		}
		var dbres=Tasklog.DB.del(rowID);
		if (!dbres) msgerror('delete error')
	}
	
	,check: function(el,forceClear){
		var row=$(el), isDone=row.hasClass('task-done'), dbres
		if (isDone===true || forceClear){ 
			row.removeClass('task-done'); 
			dbres=Tasklog.DB.update(row.attr('id'), {status:0}); 
		}
		else if (row.text().length){ 
			row.addClass('task-done'); 
			dbres=Tasklog.DB.update(row.attr('id'), {status:1}); 
		}
		if (!dbres) msgerror('check/uncheck error')
	}
	
	,up: function(div){
		div=$(div)
		var prev=div.parents('.task').prev('.task')
		if (!prev.length) return;
		prev.find('.task-description div').focus()
		window.getSelection().collapseToStart()
	}
	
	,down: function(div){
		div=$(div)
		var next=div.parents('.task').next('.task');
		if (!next.length) return;
		next.find('.task-description div').focus()
		window.getSelection().collapseToStart()
	}
	
	,onPaste: function(ev, el){
		// replace html with text on paste
		var row=$(el), rowTR=row.parents('tr');
		setTimeout(function(){
			row.html(row.text()).focus();
			try{window.getSelection().collapseToEnd()}catch(e){}
		},0); 
		clearTimeout(this.autosave); var self=this;
		this.autosave=setTimeout(function(){self.save(rowTR)},300)
	}

	,keyUpHandler: function(e,row){
		row=$(row); var rowTR=row.parents('.task');
		if ((e.which==46 || e.which==8) && !$(row).text().length){ //del || bckspc
			e.preventDefault(); 
			this.check(rowTR, true);
			this.rowRemove(row, true)
		}
		// no autosave on: shift, ctrl, alt, esc, pgup, pgdown, end, home, left, up, right, down, tab, ins, etc.
		if (e.which>=16 && e.which<=45 || e.which==9) return; 
		clearTimeout(this.autosave); var self=this;
		this.autosave=setTimeout(function(){self.save(rowTR)},300)
	}
	
	,keyHandler: function(e,row){
		var rowTR=$(row).parents('.task'), txtLen=$(row).text().length, tasksLen=rowTR.siblings('.task').length,
				wS=window.getSelection(), range=wS.getRangeAt(0), po=wS.anchorOffset
			
		//remove empty br (mozilla inserted) at the end of the editable div, if curr. ev is not ctrl+enter and there is some text
		if ($.browser.mozilla) if (txtLen&&e.which!=13){var lE=range.commonAncestorContainer.lastChild; if (lE) if(lE.nodeName=='BR') $(lE).remove()}
		
		switch(e.which){
			//bckspc
			case  8: if (txtLen==1) this.check(rowTR,true);	
						var eE=range.commonAncestorContainer, pS=eE.previousSibling
						if ((!pS || pS.nodeName!='BR') && po==0){
							e.preventDefault(); 
							if (!txtLen){ this.check(rowTR,true); this.rowRemove(row) }	
						}	
						break;
			//del
			case 46: if (!txtLen){ e.preventDefault(); this.check(rowTR,true); this.rowRemove(row,true) }	break;
						
			//tab, shift+tab
			case  9: if (e.shiftKey==true) this.up(row); else this.down(row); e.preventDefault(); break; 
			
			//enter
			case 13: if (e.shiftKey==false){ e.preventDefault(); if (txtLen){ this.save(rowTR); this.rowAdd(rowTR); this.down(row); }}
						else {
							//wS.collapseToStart()
							e.preventDefault();
							var eE=range.commonAncestorContainer, pr=(eE.parentNode.nodeName=='DIV')?eE.parentNode:eE
							br=document.createElement('br');
							pr.appendChild(br)
						} 
						break; 
			
			//esc
			case 27: if (!txtLen){e.preventDefault(); this.rowRemove(row);} break; 
			
			// ctrl+spc
			case 32: if (e.ctrlKey==true){ this.check(rowTR); e.preventDefault(); } break; 
			
			//up
			case 38: if (e.ctrlKey==true) this.moveRowUp(rowTR);
						else {
							var prev=range.startContainer.previousSibling
							if(prev)if($(prev).hasClass('tag')) prev=null;
							if (!txtLen && tasksLen>1) this.rowRemove(row)
							else if (!prev&&po==0){ e.preventDefault(); this.up(row); }
						} 
						break; 

			//down
			case 40: if (e.ctrlKey==true) this.moveRowDown(rowTR);
						else {
							if (!txtLen && tasksLen>1) this.rowRemove(row,true)
							var eE=range.commonAncestorContainer, pr=eE;
							if (pr.nodeName=='TD') pr=pr.firstChild;
							try{ while (pr.parentNode.nodeName!='TD') pr=pr.parentNode; }catch(e){ return; }							
							if (!pr) return; if (pr.nodeName!=='DIV') return;							
							var lE=pr.lastChild, ch=pr.childNodes.length;
							if (!txtLen){e.preventDefault();this.down(row)}
							else{ if (lE==eE){e.preventDefault();this.down(row)} else if( ((!po&&ch==1) || po==ch) || (lE.nodeName=='BR' && po+1==ch) || (lE.nodeName=='SPAN' && po>ch) ){e.preventDefault();this.down(row)} } 
						} 
						break;

			// ? - prevent showing help on row edit 
			case  191: e.stopPropagation(); break;
		} // switch
		
	}//keyHandler
	
}})(Tasklog);